package com.zeyuan.rpcclient.netty.helloworld01;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Scanner;

public class HelloClient {
    public static void main(String[] args) throws InterruptedException, IOException {
        NioEventLoopGroup eventExecutors = new NioEventLoopGroup();
        ChannelFuture cf=new Bootstrap()
                //添加eventloop(选择器和线程,客户端也用选择器思想，但用普通的也可以)
            .group(eventExecutors)
                //选择客户端的channel实现
            .channel(NioSocketChannel.class) //封装了jdk中nio的socket
            //添加处理器
            .handler(new ChannelInitializer<NioSocketChannel>() {
                @Override //在连接建立后被调用
                protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
                    nioSocketChannel.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
                    nioSocketChannel.pipeline().addLast(new StringEncoder());//编码器

                }
            })
                //通过channelfuture连接服务器，发起连接，激活server的accept事件(nioeventloop里面轮询是否有事件)
                .connect(new InetSocketAddress("localhost",8080));
            //这里为什么要channelfuture：异步非阻塞，main发起调用，真正执行connect的是nio线程，这个connect假设用了1s，如果不异步会在连接完成前执行下面的代码
        //所以这里要sync方法，在connect完成后才会向下执行，如果没有sync，channel就为空，执行失败
        /*除了sync还可以通过addListener方法异步处理结果，sync是main线程等待结果，addListener用监听器模式，nio线程来通知主线程执行
        * channelFuture.addListener(new ChannelFutureListener(){
        *   @override //回调方法，connect成功后就回调这个方法，执行这个方法的也是nio线程，而不是main，sync才是main
        *   public void operationComplete(ChannelFuture future) throws Exception{
        *       Channel channel=future.channel();
        *       channel.writeAndFlush("你好");
        * }
        * })
        * */
                cf.sync(); //阻塞方法，阻塞当前main线程，直到nio线程异步连接建立，拿到channel对象
                Channel c=cf.channel(); //代表连接对象
                //向服务器发送数据 发送请求把nihao字符串转为bytebuf发给server的某个eventloop
//                .writeAndFlush("nihao");
//        c.writeAndFlush("nihao");
        new Thread(()->{ //客户端不停输入并发送给服务端
            Scanner scanner=new Scanner(System.in);
            while (true){
                String s = scanner.nextLine();
                if ("q".equals(s)){
                    c.close(); //这也是异步操作，给nio操作，如果需要关闭后的后置操作，不能再这里直接写
                    break;
                }
            }
        });

        //channel关闭后的后置操作
        //两种模式：同步处理关闭 异步处理关闭
        ChannelFuture channelFuture = c.closeFuture();
        //同步方式执行关闭后的处理操作
//        channelFuture.sync(); //阻塞直到关闭close()执行完成 任务失败则抛出异常，await任务失败不会抛出异常，而是通过isSuccess方法判定
        //这里就可以写关闭之后的操作

        //异步
        channelFuture.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                //执行关闭的nio线程来执行这个方法

                //由于channel close完成后，group里面还有其他线程尚未关闭，所以线程不能退出，这里进行优雅关闭
                eventExecutors.shutdownGracefully();//停止接收新的任务，等待现有任务完成后关闭main线程
            }
        });
        channelFuture.addListener((ChannelFutureListener)cc->{
            eventExecutors.shutdownGracefully(); //lambda
        });
        System.in.read(); //阻塞等待输入


    }
}
